<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>电池数据分析-动态加载版</title>
    <!-- 引入Vue.js框架 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <!-- 引入ECharts图表库 -->
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.3.2/dist/echarts.min.js"></script>
    <!-- 引入SheetJS库，用于读取Excel文件 -->
    <script src="https://cdn.jsdelivr.net/npm/xlsx@0.17.0/dist/xlsx.full.min.js"></script>
    <!-- <script src="xlsx.full.min.js"></script> -->
    <style>
        /* 设置图表容器的样式 */

        body,
        html {
            margin: 0;
            padding: 0;
            font-family: 'Microsoft YaHei', sans-serif;
            width: 100%;
            height: 100%;
        }

        #app {
            display: flex;
            justify-content: center;
            /* 水平居中 */
            align-items: center;
            /* 垂直居中 */
            width: 100%;
            height: 100vh;
            /* 使用视口高度 */
        }

        /* 添加切换开关的样式 */

        .switch {
            position: relative;
            display: inline-block;
            width: 60px;
            height: 34px;
        }

        .switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }

        .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .4s;
            border-radius: 34px;
        }

        .slider:before {
            position: absolute;
            content: "";
            height: 26px;
            width: 26px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }

        input.data {
            font-size: 0.5em;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
        }

        input.data:hover {
            cursor: pointer;
        }

        input:checked+.slider {
            background-color: #2196F3;
        }

        input:checked+.slider:before {
            transform: translateX(26px);
        }

        /* 自定义单选按钮样式 */

        .radio-group {
            display: flex;
            gap: 20px;
            background: rgba(255, 255, 255, 0.1);
            padding: 10px 20px;
            border-radius: 8px;
            /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
        }

        .radio-label {
            display: flex;
            align-items: center;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .radio-label input[type="radio"] {
            display: none;
        }

        .radio-custom {
            width: 20px;
            height: 20px;
            border: 2px solid #ddd;
            border-radius: 50%;
            margin-right: 8px;
            position: relative;
            transition: all 0.3s ease;
        }

        .radio-custom::after {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%) scale(0);
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: #2196F3;
            transition: all 0.2s ease;
        }

        .radio-label input[type="radio"]:checked+.radio-custom {
            border-color: #2196F3;
        }

        .radio-label input[type="radio"]:checked+.radio-custom::after {
            transform: translate(-50%, -50%) scale(1);
        }

        .radio-label:hover .radio-custom {
            border-color: #2196F3;
            box-shadow: 0 0 5px rgba(33, 150, 243, 0.3);
        }

        .radio-text {
            font-size: 14px;
            color: #333;
            user-select: none;
        }

        /* 添加标题样式 */

        .title-container {
            position: absolute;
            left: 8%;
            top: 20px;
            z-index: 10;
            background: rgba(255, 255, 255, 0.1);
            padding: 10px 20px;
            border-radius: 8px;
            /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
            transition: all 0.3s ease;
        }

        .title-text {
            font-size: 18px;
            font-weight: bold;
            font-style: italic;
            color: #2196F3;
            margin: 0;
            user-select: none;
        }

        .title-container:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(33, 150, 243, 0.2);
        }

        /* 添加加载动画样式 */
        #loading {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(255, 255, 255, 0.8);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9999;
        }

       .spinner {
            border: 16px solid #f3f3f3;
            border-top: 16px solid #3498db;
            border-radius: 50%;
            width: 120px;
            height: 120px;
            animation: spin 2s linear infinite;
        }

        @keyframes spin {
            0% {
                transform: rotate(0deg);
            }
            100% {
                transform: rotate(360deg);
            }
        }
    </style>
</head>

<body>
    <!-- 加载动画 -->
    <div id="loading">
        <div class="spinner"></div>
    </div>

    <!-- Vue应用的根元素 -->
    <div id="app">
        <!-- 添加标题容器 -->
        <div class="title-container">
            <h1 class="title-text">TP-BMS电芯测试数据图表分析</h1>
        </div>

        <div style="position: absolute; right: 20%; top: 20px; z-index: 10;" class="radio-group" title="切换数据表">
            <label class="radio-label">
                <input type="radio" v-model="dataType" value="charge" @change="loadExcelData">
                <span class="radio-custom"></span>
                <span class="radio-text">充电数据分析</span>
            </label>
            <label class="radio-label">
                <input type="radio" v-model="dataType" value="discharge" @change="loadExcelData">
                <span class="radio-custom"></span>
                <span class="radio-text">放电数据分析</span>
            </label>
        </div>
        <!-- 图表将被渲染到这个div中 -->
        <div ref="chart" style="width: 100%; height: 100%;"></div>
    </div>

    <script>
        // 创建Vue实例
        new Vue({
            el: '#app',
            data: {
                chart: null, // 用于存储ECharts实例
                excelData: null, // 用于存储从Excel文件读取的数据
                dataType: 'charge', // 默认选择充电数据表
                isLoading: true // 添加加载状态
            },
            mounted() {
                // 组件挂载后立即加载Excel数据
                this.loadExcelData().finally(() => {
                    // 数据加载完成后隐藏加载动画
                    this.isLoading = false;
                    document.getElementById('loading').style.display = 'none';
                });
            },
            methods: {
                // 加载Excel数据的方法
                async loadExcelData() {
                    // 显示加载动画
                    this.isLoading = true;
                    document.getElementById('loading').style.display = 'flex';
                    try {
                        const fileName = this.dataType === 'charge' ? '充电.xlsx' : '放电.xlsx'; // 根据选择的类型选择文件
                        const workbook = await this.loadExcelFile(fileName);
                        const firstSheetName = workbook.SheetNames[0];
                        const worksheet = workbook.Sheets[firstSheetName];
                        this.excelData = XLSX.utils.sheet_to_json(worksheet, {
                            header: 1
                        });

                        this.initChart(); // 初始化图表
                    } catch (error) {
                        console.error(error);
                    } finally {
                        // 数据加载完成后隐藏加载动画
                        this.isLoading = false;
                        document.getElementById('loading').style.display = 'none';
                    }
                },
                // 初始化ECharts图表
                initChart() {
                    this.chart = echarts.init(this.$refs.chart);
                    const option = this.getChartOption();
                    this.chart.setOption(option);
                },
                // 获取图表配置选项
                getChartOption() {
                    // 获取各列的索引
                    const timeColumn = this.excelData[0].indexOf('时间');
                    const currentColumn = this.excelData[0].indexOf('电流');
                    const socColumn = this.excelData[0].indexOf('SOC');
                    const capacityColumn = this.excelData[0].indexOf('剩余容量');
                    const voltageColumn = this.excelData[0].indexOf('最高电压');
                    const voltageMinColumn = this.excelData[0].indexOf('最低电压');
                    const temperatureColumn = this.excelData[0].indexOf('最高温度');

                    // 去掉表头，只保留数据行
                    const data = this.excelData.slice(1);
                    const totalPoints = data.length;
                    // 如果总数据点少于30，直接使用全部数据点；否则保证至少30个点
                    const selectedPoints = totalPoints < 30 ? totalPoints : Math.max(30, Math.floor(totalPoints / 100));

                    // 计算采样步长
                    const step = Math.floor(totalPoints / (selectedPoints - 1));

                    // 初始化数据数组
                    const xAxisData = [];
                    const currentData = [];
                    const socData = [];
                    const capacityData = [];
                    const voltageData = [];
                    const voltageMinData = [];
                    const temperatureData = [];

                    // 格式化时间函数
                    function formatTime(timeValue) {
                        // 检查时间值是否为数字
                        if (typeof timeValue === 'number') {
                            // 将Excel时间数值转换为日期对象
                            const date = new Date((timeValue - 25569) * 86400 * 1000); // Excel日期基准为1899年12月30日
                            const hours = date.getUTCHours().toString().padStart(2, '0');
                            const minutes = date.getUTCMinutes().toString().padStart(2, '0');
                            return `${hours}:${minutes}`; // 返回HH:mm格式
                        }
                        return timeValue; // 如果格式不正确，返回原始值
                    }

                    // 采样数据
                    for (let i = 0; i < selectedPoints; i++) {
                        // 确保最后一个点是数据的最后一行
                        const index = i === selectedPoints - 1 ? totalPoints - 1 : i * step;
                        const row = data[index];
                        const timeValue = row[timeColumn];
                        xAxisData.push(formatTime(timeValue));

                        // 处理电流数据 负数为放电，正数为充电  负数翻转为正数
                        let currentValue = row[currentColumn];
                        if (this.dataType === 'discharge') {
                            currentValue = currentValue > 0 ? -currentValue : Math.abs(currentValue);
                        }
                        currentData.push(currentValue);

                        socData.push(row[socColumn]);
                        capacityData.push(row[capacityColumn]);
                        voltageData.push(row[voltageColumn]);
                        voltageMinData.push(row[voltageMinColumn]);
                        temperatureData.push(row[temperatureColumn]);

                        // 添加隐藏的小刻度值
                        for (let j = 1; j <= 4; j++) {
                            const smallIndex = index + (j * step / 5); // 计算小刻度的索引
                            if (smallIndex < totalPoints) {
                                const smallRow = data[Math.floor(smallIndex)];
                                const smallTimeValue = smallRow[timeColumn];
                                xAxisData.push(formatTime(smallTimeValue));

                                // 处理小刻度的电流数据
                                let smallCurrentValue = smallRow[currentColumn];
                                if (this.dataType === 'discharge') {
                                    smallCurrentValue = smallCurrentValue > 0 ? -smallCurrentValue : Math.abs(smallCurrentValue);
                                }
                                currentData.push(smallCurrentValue);

                                socData.push(smallRow[socColumn]);
                                capacityData.push(smallRow[capacityColumn]);
                                voltageData.push(smallRow[voltageColumn]);
                                voltageMinData.push(smallRow[voltageMinColumn]);
                                temperatureData.push(smallRow[temperatureColumn]);
                            }
                        }
                    }

                    // 返回ECharts配置选项
                    return {
                        grid: {
                            left: '2%', // 设置左边距为100px
                            right: '10%', // 右边距
                            top: '10%', // 上边距
                            bottom: '5%', // 下边距
                            containLabel: true // 包含坐标轴标签
                        },
                        // 配置提示框
                        tooltip: {
                            trigger: 'axis',
                            axisPointer: {
                                type: 'cross',
                                label: {
                                    backgroundColor: '#6a7985'
                                }
                            },
                            formatter: function (params) {
                                let result = params[0].axisValue + '<br/>';
                                params.forEach(function (item) {
                                    let unit = '';
                                    switch (item.seriesName) {
                                        case '电流':
                                            unit = 'A';
                                            break;
                                        case 'SOC':
                                            unit = '%';
                                            break;
                                        case '容量':
                                            unit = 'Ah';
                                            break;
                                        case '最高电压':
                                            unit = 'V';
                                            break;
                                        case '最低电压':
                                            unit = 'V';
                                            break;
                                        case '最高温度':
                                            unit = '°C';
                                            break;
                                    }
                                    result += item.marker + item.seriesName + ': ' + item.value + ' ' + unit + '<br/>';
                                });
                                return result;
                            },
                        },

                        // 配置图例
                        legend: {
                            data: ['电流', 'SOC', '容量', '最高电压', '最低电压', '最高温度']
                        },
                        // 配置X轴
                        xAxis: {
                            type: 'category',
                            boundaryGap: false,
                            data: xAxisData,
                            axisLabel: {
                                formatter: function (value, index) {
                                    // 只显示第一个和最后一个刻度值，其余刻度值每隔一个隐藏三个
                                    if (index === 0 || index === xAxisData.length - 1 || index % 4 === 0) {
                                        return value; // 返回刻度值
                                    }
                                    return ''; // 隐藏刻度值
                                },
                                interval: 0,
                                rotate: 45,
                                textStyle: {
                                    fontSize: '0.5em', // 设置刻度文本大小
                                    color: '#333' // 设置刻度文本颜色
                                }
                            },
                            splitLine: {
                                show: true,
                                lineStyle: {
                                    color: 'rgba(0, 0, 0, 0.4)', // 网格线颜色
                                    width: 0.25, // 网格线宽度
                                    type: 'solid' // 网格线类型
                                }
                            },
                            axisTick: {
                                show: true, // 显示主刻度
                                inside: true // 主刻度在内部
                            },
                            splitNumber: 5 // 每个区间分成5个小刻度
                        },
                        // 配置Y轴
                        yAxis: [
                            {
                                type: 'value',
                                name: 'Current(A)',
                                position: 'left', // 电流在左边
                                nameGap: 20, // 增加名称与轴线之间的距离
                                axisLabel: {
                                    textStyle: {
                                        color: '#FF0000' // 电流的颜色
                                    },
                                    padding: [0, 0, 0, 0] // 设置内边距 [上, 右, 下, 左]
                                },
                                splitLine: {
                                    show: true,
                                    lineStyle: {
                                        color: 'rgba(0, 0, 0, 0.5)', // 网格线颜色
                                        width: 0.51, // 网格线宽度
                                        type: 'solid' // 网格线类型
                                    }
                                },
                                nameTextStyle: {
                                    color: '#FF0000', // 电流标题颜色
                                    fontSize: 10, // 设置字体大小
                                    // fontWeight: 'bold' // 设置字体粗细
                                }
                            },
                            {
                                type: 'value',
                                name: 'SOC%',
                                position: 'right', // SOC在右边
                                offset: 30, // 调整位置
                                nameGap: 20, // 增加名称与轴线之间的距离
                                axisLabel: {
                                    textStyle: {
                                        color: '#228B22' // SOC的颜色改为森林绿
                                    },
                                    padding: [0, 0, 0, -15] // 设置内边距 [上, 右, 下, 左]
                                },
                                splitLine: {
                                    show: true,
                                    lineStyle: {
                                        color: 'rgba(0, 0, 0, 0.2)', // 网格线颜色
                                        width: 0.51, // 网格线宽度什
                                        type: 'solid' // 网格线类型
                                    }
                                },
                                nameTextStyle: {
                                    color: '#228B22', // SOC标题颜色改为森林绿
                                    fontSize: 10, // 设置字体大小
                                    // fontWeight: 'bold' // 设置字体粗细
                                }
                            },
                            {
                                type: 'value',
                                name: 'AH',
                                position: 'right', // 剩余容量在右边
                                offset: 80, // 调整位置
                                nameGap: 20, // 增加名称与轴线之间的距离
                                axisLabel: {
                                    textStyle: {
                                        color: '#0000FF' // 容量的颜色
                                    },
                                    padding: [0, 0, 0, -15] // 设置内边距 [上, 右, 下, 左]
                                },
                                splitLine: {
                                    show: true,
                                    lineStyle: {
                                        color: 'rgba(0, 0, 0, 0.5)', // 网格线颜色
                                        width: 0.51, // 网格线宽度
                                        type: 'solid' // 网格线类型
                                    }
                                },
                                nameTextStyle: {
                                    color: '#0000FF', // 容量标题颜色
                                    fontSize: 10, // 设置字体大小
                                    // fontWeight: 'bold' // 设置字体粗细
                                }
                            },
                            {
                                type: 'value',
                                name: 'MaxV',
                                position: 'right',
                                offset: 130,
                                nameGap: 20,
                                max: function (value) {
                                    // 向上取整到最接近的整数
                                    return Math.ceil(value.max);
                                },
                                axisLabel: {
                                    textStyle: {
                                        color: 'rgba(255, 140, 0, 1)',
                                    },
                                    padding: [0, 0, 0, -15]
                                },
                                splitLine: {
                                    show: true,
                                    lineStyle: {
                                        color: 'rgba(0, 0, 0, 0.2)',
                                        width: 0.51,
                                        type: 'solid'
                                    }
                                },
                                nameTextStyle: {
                                    color: 'rgba(255, 140, 0, 1)',
                                    fontSize: 10,
                                }
                            },
                            {
                                type: 'value',
                                name: 'MinV',
                                position: 'right',
                                offset: 180,
                                nameGap: 20,
                                max: function (value) {
                                    // 使用最高电压轴的上限值
                                    const maxVoltage = Math.max(...voltageData);
                                    return Math.ceil(maxVoltage);
                                },
                                axisLabel: {
                                    textStyle: {
                                        color: '#1E90FF'
                                    },
                                    padding: [0, 0, 0, -15]
                                },
                                splitLine: {
                                    show: true,
                                    lineStyle: {
                                        color: 'rgba(0, 0, 0, 0.2)',
                                        width: 0.51,
                                        type: 'solid'
                                    }
                                },
                                nameTextStyle: {
                                    color: '#1E90FF',
                                    fontSize: 10,
                                }
                            },
                            {
                                type: 'value',
                                name: '℃',
                                position: 'right',
                                offset: 210, // 调整位置，为最低电压腾出空间
                                nameGap: 20,
                                axisLabel: {
                                    textStyle: {
                                        color: '#800080'
                                    },
                                    padding: [0, 0, 0, -15]
                                },
                                splitLine: {
                                    show: true,
                                    lineStyle: {
                                        color: 'rgba(0, 0, 0, 0.5)',
                                        width: 0.51,
                                        type: 'solid'
                                    }
                                },
                                nameTextStyle: {
                                    color: '#800080',
                                    fontSize: 10,
                                }
                            }
                        ],
                        // 配置数据系列
                        series: [
                            {
                                name: '电流',
                                type: 'line',
                                data: currentData,
                                yAxisIndex: 0,
                                itemStyle: {
                                    color: '#FF0000' // 曲线颜色
                                },
                                lineStyle: {
                                    width: 1, // 曲线宽度
                                    type: 'solid' // 曲线类型
                                },
                                symbol: 'circle', // 数据点形状
                                symbolSize: 3, // 数据点大小
                            },
                            {
                                name: 'SOC',
                                type: 'line',
                                data: socData,
                                yAxisIndex: 1,
                                itemStyle: {
                                    color: '#228B22' // SOC曲线颜色改为森林绿
                                },
                                lineStyle: {
                                    width: 1, // 曲线宽度
                                    type: 'solid' // 曲线类型
                                },
                                symbol: 'circle', // 数据点形状
                                symbolSize: 3, // 数据点大小
                            },
                            {
                                name: '容量',
                                type: 'line',
                                data: capacityData,
                                yAxisIndex: 2,
                                itemStyle: {
                                    color: '#0000FF' // 曲线颜色
                                },
                                lineStyle: {
                                    width: 1, // 曲线宽度
                                    type: 'solid' // 曲线类型
                                },
                                symbol: 'circle', // 数据点形状
                                symbolSize: 3, // 数据点大小
                            },
                            {
                                name: '最高电压',
                                type: 'line',
                                data: voltageData,
                                yAxisIndex: 3,
                                itemStyle: {
                                    color: 'rgba(255, 140, 0, 1)', // 曲线颜色
                                },
                                lineStyle: {
                                    width: 1, // 曲线宽度
                                    type: 'solid' // 曲线类型
                                },
                                symbol: 'circle', // 数据点形状
                                symbolSize: 3, // 数据点大小
                            },
                            {
                                name: '最低电压',
                                type: 'line',
                                data: voltageMinData,
                                yAxisIndex: 4,
                                itemStyle: {
                                    color: '#1E90FF'
                                },
                                lineStyle: {
                                    width: 1,
                                    type: 'solid'
                                },
                                symbol: 'circle',
                                symbolSize: 3,
                            },
                            {
                                name: '最高温度',
                                type: 'line',
                                data: temperatureData,
                                yAxisIndex: 5,
                                itemStyle: {
                                    color: '#800080' // 曲线颜色
                                },
                                lineStyle: {
                                    width: 1, // 曲线宽度
                                    type: 'solid' // 曲线类型
                                },
                                symbol: 'circle', // 数据点形状
                                symbolSize: 3, // 数据点大小
                            }
                        ],
                        // 添加文本标签显示取样数量 totalPoints
                        graphic: [
                            {
                                type: 'text',
                                left: '10%',
                                top: 'bottom',
                                style: {
                                    text: `时间线: ${selectedPoints}, 取样数: ${selectedPoints * 3}, 总样数: ${totalPoints}`,
                                    font: '12px Microsoft YaHei',
                                    fill: '#333'
                                }
                            },
                            {
                                type: 'text',
                                left: 'right',
                                top: 'bottom',
                                style: {
                                    text: '电池测试数据分析工具V1.0 Wic-power@2025 更新时间: ' + new Date().toLocaleString(),
                                    font: '12px Microsoft YaHei',
                                    fill: '#333'
                                }
                            }
                        ]
                    };
                },
                async loadExcelFile(fileName) {
                    const xhr = new XMLHttpRequest();
                    xhr.open('GET', fileName, true);
                    xhr.responseType = 'arraybuffer'; // 确保以二进制格式读取

                    return new Promise((resolve, reject) => {
                        xhr.onload = (e) => {
                            if (xhr.status === 200) {
                                const data = new Uint8Array(xhr.response);
                                const workbook = XLSX.read(data, {
                                    type: 'array'
                                });
                                resolve(workbook); // 解析成功，返回工作簿
                            } else {
                                reject(new Error('Error loading the Excel file.'));
                            }
                        };

                        xhr.onerror = () => {
                            reject(new Error('Error loading the Excel file.'));
                        };

                        xhr.send();
                    });
                }
            }
        });
    </script>
</body>

</html>